package
{
	import com.adobe.images.JPGEncoder;
	import com.quasimondo.bitmapdata.CameraBitmap;
	
	import flash.debugger.enterDebugger;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Loader;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.external.ExternalInterface;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.SharedObject;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	import jp.maaash.ObjectDetection.ObjectDetector;
	import jp.maaash.ObjectDetection.ObjectDetectorEvent;
	import jp.maaash.ObjectDetection.ObjectDetectorOptions;
	
	import mx.containers.Canvas;
	import mx.controls.Alert;
	import mx.core.UIComponent;
	import mx.events.CloseEvent;
	import mx.utils.Base64Decoder;
	
	import ru.inspirit.surf.ASSURF;
	import ru.inspirit.surf.IPoint;
	import ru.inspirit.surf.SURFOptions;
	import ru.inspirit.surf_example.FlashSURFExample;
	import ru.inspirit.surf_example.MatchElement;
	import ru.inspirit.surf_example.MatchList;
	import ru.inspirit.surf_example.utils.QuasimondoImageProcessor;
	import ru.inspirit.surf_example.utils.RegionSelector;
	import ru.inspirit.surf_example.utils.SURFUtils;
	
	public class FaceDetector extends Canvas
	{
		
		public var detector    :ObjectDetector;
		public var options     :ObjectDetectorOptions;	
		public var view :Sprite;
		public var faceRectContainer :Sprite;
		public var camera:CameraBitmap;
		public var detectionMap:BitmapData;
		public var drawMatrix:Matrix;
		public var scaleFactor:int = 4;
		public var w:int = 640;
		public var h:int = 480;
		public var detectedRect:Rectangle = null;		
		//public var So:SharedObject= SharedObject.getLocal("TempImage");
		//[Bindable]
		//public var ba:ByteArray;
		//public var detectedFace:BitmapData;
		//[Bindable]
		//public var bmp:Bitmap=null;
		[Bindable]
		public var spriteContainer:UIComponent;
		[Bindable]
		public var pointsEnShowCam:Boolean;
		[Bindable]
		public var pointsEnAdSpace:Boolean;
		
		[Bindable]
		public var entEn:Boolean=false;
		[Bindable]
		public var regEn:Boolean=false;
		[Bindable]
		public var selLabel:String = "Select Region";
		
		public var recty:int=0;
		public var flag:int=0;

		public static const SCALE:Number = 1.5;
		public static const INVSCALE:Number = 1 / SCALE;
		
		public static const SCALE_MAT:Matrix = new Matrix(1/SCALE, 0, 0, 1/SCALE, 0, 0);
		
		protected var overlay:Shape;
		public var surf:ASSURF;
		public var surfOptions:SURFOptions;
		public var quasimondoProcessor:QuasimondoImageProcessor;
		public var buffer:BitmapData;
		public var autoCorrect:Boolean = false;
		[Bindable]
		public var matchList:MatchList;
		[Bindable]
		public var matched:Vector.<MatchElement>;
		public var byteArrayDB:ByteArray;
		protected var matchView:Sprite;
		public var rectDB:Rectangle;
		[Bindable]
		public var bmpData:BitmapData;
		[Bindable]
		public var bmpDataSelect:BitmapData;
		public var regionSelect:RegionSelector;
		[Bindable]
		public var enterLabel:String="Wait...";	
		[Bindable]
		public var resultArray:ByteArray;
		[Bindable]
		public var matchBundleArray:ByteArray;
		public var elementsCount:int=0;
		public var resultPos:int=0;
		public var tempPos:int=0;
		public static const POINT_SIZE:int = 69 << 3;
		
		public function FaceDetector()
		{
			surfOptions = new SURFOptions(int(640 / SCALE), int(480 / SCALE), 200, 0.001, true, 4, 4, 2);
			surf = new ASSURF(surfOptions);
			
			surf.pointMatchFactor = 0.3;
			surf.pointsThreshold = 0.001;
			autoCorrect = true;
			surf.imageProcessor = autoCorrect ? quasimondoProcessor : null;
			
			buffer = new BitmapData(surfOptions.width, surfOptions.height, false, 0x00);
			buffer.lock();
			
			quasimondoProcessor = new QuasimondoImageProcessor(buffer.rect);
			
			matchList = new MatchList(surf);
			
			//load from database
			recognition();
			
			init();
			initDetector();
			camera.addEventListener(Event.RENDER, render);
		}
		
		public function loadData():void {
			
			SURFUtils.openPointsDataFile(loadPointsDone);
		}
		
		protected function loadPointsDone(data:ByteArray):void 
		{
			matchList.initListFromByteArray(data);
		}

		protected function render( e:Event ) : void
		{
			if(pointsEnShowCam==true || pointsEnAdSpace==true){
				var gfx:Graphics = overlay.graphics;
				buffer.draw(camera.bitmapData, SCALE_MAT);
				var ipts:Vector.<IPoint> = surf.getInterestPoints(buffer);
			
				gfx.clear();
				SURFUtils.drawIPoints(gfx, ipts, SCALE);
			
				matched = matchList.getMatches();

				SURFUtils.drawMatchedBitmaps(matched, matchView);
				pointsEn=false;
				if(matched.length==1 && pointsEnShowCam==true){
					entEn=true;
					pointsEnShowCam=false;
					enterLabel="Enter";
					gfx.clear();
			
					trace('FOUND POINTS: ' + surf.currentPointsCount + '  POINTS TO MATCH: ' + matchList.pointsCount + '  elementsCount: ' + matchList.elementsCount+' matched.length: ' + matched.length+' matched.id: ' + matched[0].id+' matched.matchCount: ' + matched[0].matchCount+' matched.pointsCount: ' + matched[0].pointsCount );
		
				}
			}
		}

		public function init():void{
			
			view = new Sprite();
			camera = new CameraBitmap( w, h, 15 );
			camera.addEventListener( Event.RENDER, cameraReadyHandler );
			view.addChild( new Bitmap( camera.bitmapData ) );
			
			overlay = new Shape();
			view.addChild(overlay);
			
			detectionMap = new BitmapData( w / scaleFactor, h / scaleFactor, false, 0 );
			drawMatrix = new Matrix( 1/ scaleFactor, 0, 0, 1 / scaleFactor );
			
			faceRectContainer = new Sprite();
			view.addChild( faceRectContainer);
			
			spriteContainer= new UIComponent();
			spriteContainer.addChild(view);
			
			matchView = new Sprite();
			matchView.x = 640;
			view.addChild(matchView);
		
			regionSelect = new RegionSelector(new Rectangle(0, 0, 640, 480));
			view.addChild(regionSelect);
			
		}
		
		public function cameraReadyHandler( event:Event ):void
		{
			detectionMap.draw(camera.bitmapData,drawMatrix,null,"normal",null,true);
			detector.detect( detectionMap );
		}
		
		public function initDetector():void
		{
			detector = new ObjectDetector();
			
			var options:ObjectDetectorOptions = new ObjectDetectorOptions();
			options.min_size  = 30;
			detector.options = options;
			detector.addEventListener(ObjectDetectorEvent.DETECTION_COMPLETE, detectionHandler );
		}
		
		
		public function detectionHandler( e :ObjectDetectorEvent ):void
		{
			var g :Graphics = faceRectContainer.graphics;
			g.clear();
			if( e.rects ){
				g.lineStyle( 2 );	// black 2pix
				e.rects.forEach( function( r :Rectangle, idx :int, arr :Array ) :void {
					detectedRect = r;
					
					if(pointsEnShowCam){
						g.drawRect( r.x * scaleFactor, r.y * scaleFactor, r.width * scaleFactor, r.height * scaleFactor );
					
						if(flag==0){
							recty=r.y * scaleFactor;
							flag=1;
						}else{
							recty+=r.height/4;
						}
					
						if(recty < (r.y * scaleFactor) + (r.height * scaleFactor)- (r.height/4) ){
						
							g.lineStyle(1, 0xFFFFFF, .6);
							g.beginFill(0xFFFFFF, 0.5);
							g.drawRect(r.x * scaleFactor, recty , r.width * scaleFactor, r.height/4);
							g.endFill();
						}else
							recty=r.y * scaleFactor;
					}
				});
			}else{
				detectedRect = null;
			}
		}

		
		private function nextPage(event:CloseEvent) : void
		{
			dispatchEvent( new Event( Event.CHANGE ) );
		}
		
		public function isRecognized():void{
						
			Alert.okLabel = "NEXT";
				
			Alert.show("IMAGE AUTHENTICATION COMPLETED","Success", Alert.OK, this, nextPage);
			
			
		}
		protected function onSelectRegion(e:Event = null):void
		{
			if(!regionSelect.visible)
			{
				regionSelect.init();
				camera.active = false;
				selLabel="Add Region";
				regEn=false;
				
			} else{
				matchList.clear();	
				if(regionSelect.rect.width > 20 && regionSelect.rect.height > 20)
				{
					bmpDataSelect = new BitmapData(regionSelect.rect.width, regionSelect.rect.height, false, 0x00);
					bmpDataSelect.copyPixels(camera.bitmapData, regionSelect.rect, new Point(0, 0));
					
					regionSelect.rect.x *= INVSCALE;
					regionSelect.rect.y *= INVSCALE;
					regionSelect.rect.width *= INVSCALE;
					regionSelect.rect.height *= INVSCALE;
					
					matchList.addRegionAsMatch(regionSelect.rect, bmpDataSelect);
					
				}
				
				regionSelect.uninit();
				camera.active = true;
				selLabel="Select Region";
				regEn=true;
			}
		}
		
		public function recognition():void{
			
			resultArray = new ByteArray();
			matchBundleArray = new ByteArray();
			var xmlURL:URLRequest = new URLRequest("http://localhost/identification-debug/services/getAllUsers.php");
			var xmlLoader:URLLoader = new URLLoader(xmlURL);
			xmlLoader.addEventListener(Event.COMPLETE, getUsersComplete);
			
		}
		public function getUsersComplete (event:Event):void{
			var numRows:int;
			var numCols:int;
			var i:int;
			var j:int;
			
			var userString:String = event.target.data;
			var array:Array = userString.split(",");
			for(i=0;i<array.length;i++){
				if(array[i]!=""){
					var xmlURL:URLRequest = new URLRequest("http://localhost/identification-debug/services/getImages.php?Input="+array[i]);
					var xmlLoader:URLLoader = new URLLoader(xmlURL);
					xmlLoader.addEventListener(Event.COMPLETE, loadImageDB);
					trace("username:  " + array[i] );
				}
			}

		}
		public function loadImageDB(evt:Event):void {
			elementsCount++;
			byteArrayDB=new ByteArray();
			var dbString:String = evt.target.data ;
			var dec:Base64Decoder = new Base64Decoder();
			dec.decode(dbString);
			byteArrayDB=dec.toByteArray();
			byteArrayDB.uncompress();
			byteArrayDB.position = 0;
			byteArrayDB.readInt();
			resultArray.position = resultPos;
			if(elementsCount == 1){
				//skip elementsCount number for every database entry to make result byte array havin only one elementsCount
				resultArray.writeInt(0);
			}
			
			var pointsCount:int = byteArrayDB.readInt();//pointsCount of every entry
			resultArray.writeInt(pointsCount);
			var width:int = byteArrayDB.readInt();//width & height of image stored in db for each person
			var height:int = byteArrayDB.readInt();
			resultArray.writeInt(width);
			resultArray.writeInt(height);
			var bmap:BitmapData = new BitmapData(width, height, false, 0x00);
			bmap.setPixels(bmap.rect, byteArrayDB);
			resultArray.writeBytes(bmap.getPixels(bmap.rect));
			if(elementsCount>1)
				resultPos +=byteArrayDB.position-4;
			else
				resultPos +=byteArrayDB.position;
			//matchBundle of each record consisting points of interest
			//here collecting each matchbundle of each record into one resultant matchbundle
			matchBundleArray.writeBytes(byteArrayDB, byteArrayDB.position, pointsCount * POINT_SIZE);
			
		}
		
			
	/*	public function extractFace( ):void
		{	
			var encoder:JPGEncoder = new JPGEncoder();
			
			var detectedByte:ByteArray = camera.bitmapData.getPixels(detectedRect);
			detectedByte.position = 0;
			
			detectedFace = new BitmapData( detectedRect.width, detectedRect.height, true );
			
			for( var i:uint=0; i<detectedRect.height ; i++ )
			{
				for( var j:uint=0; j<detectedRect.width; j++ )
				{
					detectedFace.setPixel( j, i, detectedByte.readUnsignedInt() );
				}
			}
			
			ba= encoder.encode(detectedFace);
			ba.position=0;
			saveImage();
			
		}
		
		private function saveImage():void {
		
		So.data.image = ba;
		try {
		So.flush();
		} catch (error:Error) {
		trace("Error...Could not write SharedObject to disk\n");
		}
		
		loadImage(So.data.image);
		
		}
		
		public function loadImage(imgSo:ByteArray):void{
			var imgLoader:Loader = new Loader();
			imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,displayImage);
			imgLoader.loadBytes(imgSo);
		}
		
		public function displayImage(event:Event):void {
		
			bmp= event.currentTarget.content as Bitmap;
			bmp.smoothing=true;
		}
	*/
		
		
	}
}